###############################################################################
#                                                                             #
#    This file is part of the "ssbot" Python module.                          #
#    It is distributed under the MIT License. See LICENSE.txt for details.    #
#                                                                             #
###############################################################################

import os
import os.path
import string

import BaseHTTPServer

import ssutils


##################################################
##################################################

UI_STYLE = (
"""
tr.%s
{
    background-color: #00CC66;
    border-style: solid;
    border-color: #000000;
    border-top-width: 4px;
    border-bottom-width: 4px;
    border-left-width: 0px;
    border-right-width: 0px;
}

tr.%s
{
    background-color: #FF6666;
    border-style: solid;
    border-color: #000000;
    border-top-width: 4px;
    border-bottom-width: 4px;
    border-left-width: 0px;
    border-right-width: 0px;
}

tr.%s
{
    background-color: #FFFF66;
    border-style: solid;
    border-color: #000000;
    border-top-width: 4px;
    border-bottom-width: 4px;
    border-left-width: 0px;
    border-right-width: 0px;
}

tr.%s
{
    background-color: #99CCFF;
    border-style: solid;
    border-color: #000000;
    border-top-width: 4px;
    border-bottom-width: 4px;
    border-left-width: 0px;
    border-right-width: 0px;
}

tr.%s
{
    background-color: #FF99FF;
    border-style: solid;
    border-color: #000000;
    border-top-width: 4px;
    border-bottom-width: 4px;
    border-left-width: 0px;
    border-right-width: 0px;
}

td.summary_cat
{
    text-align: right;
    border-width: 0px;
    padding-left: 50px;
    padding-right: 5px;
    padding-top: 5px;
    padding-bottom: 5px;
}

td.summary_val
{
    text-align: left;
    border-width: 0px;
    padding-left: 5px;
    padding-right: 50px;
    padding-top: 5px;
    padding-bottom: 5px;
}

td.stats
{
    text-align: center;
    border-style: solid;
    border-color: #FFFFFF;
    border-left-width: 1px;
    border-right-width: 1px;
    border-bottom-width: 0px;
    border-top-width: 0px;
    padding-left: 50px;
    padding-right: 50px;
    padding-top: 5px;
    padding-bottom: 5px;
}

table
{
    border-collapse: collapse;
    border-spacing: 0;
    border-width: 0;
}
""" % (ssutils.RESULT_PASS, ssutils.RESULT_FAIL, ssutils.STATUS_RUNNING,
       ssutils.STATUS_PENDING, ssutils.RESULT_EXCEPTION)
)

BUILDER_PAGE_TEMPLATE = string.Template(
"""
<HTML>
<HEAD>
<TITLE>$builder_name</TITLE>
</HEAD>
<STYLE>
""" +
UI_STYLE +
"""
</STYLE>
<BODY>
<H2>$builder_name</H2>
<HR>
<P>
<B>Status: </B>Active
</P>
<HR>
$builds_table
</BODY>
</HTML>
"""
)

BUILDS_TABLE_ROW_TEMPLATE = string.Template(
"""
<TR class="$row_class">
<TD class="$cell_class">
<A href="builds/$build_number">$build_number</A>
</TD>
<TD class="$cell_class">
<pre>
$blame
</pre>
</TD>
<TD class="$cell_class">
$result
</TD>
<TD class="$cell_class">
$start_time
</TD>
<TD class="$cell_class">
$end_time
</TD>
</TR>
"""
)

BUILDS_TABLE_TEMPLATE = string.Template(
"""
<TABLE>
<TR>
<TD class="stats">
<B>Build</B>
</TD>
<TD class="stats">
<B>Blame</B>
</TD>
<TD class="stats">
<B>Result</B>
</TD>
<TD class="stats">
<B>Start Time</B>
</TD>
<TD class="stats">
<B>End Time</B>
</TD>
</TR>
$build_rows
</TABLE>
"""
)

###############################################
###############################################

BUILD_SUMMARY_TABLE_TEMPLATE = string.Template(
"""
<TABLE>
<TR>
<TD class="summary_cat">
<B>Status</B>
</TD>
<TD class="summary_val">
$status
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>Start Time:</B>
</TD>
<TD class="summary_val">
$start_time
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>End Time:</B>
</TD>
<TD class="summary_val">
$end_time
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>Result:</B>
</TD>
<TD class="summary_val">
$result
</TD>
</TR>
</TABLE>
"""
)

BUILD_TABLE_ROW_TEMPLATE = string.Template(
"""
<TR class="$row_class">
<TD class="stats">
<A href="$step_url">$step_name</A>
</TD>
<TD class="stats">
$result
</TD>
<TD class="stats">
$start_time
</TD>
<TD class="stats">
$end_time
</TD>
</TR>
"""
)

BUILD_TABLE_TEMPLATE = string.Template(
"""
<TABLE>
<TR>
<TD class="stats">
<B>Steps</B>
</TD>
<TD class="stats">
<B>Result</B>
</TD>
<TD class="stats">
<B>Start Time</B>
</TD>
<TD class="stats">
<B>End Time</B>
</TD>
</TR>
$step_rows
</TABLE>
"""
)

BUILD_PAGE_TEMPLATE = string.Template(
"""
<HTML>
<HEAD>
<TITLE>$builder_name : Build $build_number</TITLE>
</HEAD>
<STYLE>
""" +
UI_STYLE +
"""
</STYLE>
<BODY>
<H2>$builder_name : Build $build_number</H2>
<HR>
$summary_table
<HR>
$build_table
</BODY>
</HTML>
"""
)

##################################################
##################################################

STEP_SUMMARY_TABLE_TEMPLATE = string.Template(
"""
<TABLE>
<TR>
<TD class="summary_cat">
<B>Status</B>
</TD>
<TD class="summary_val">
$status
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>Start Time:</B>
</TD>
<TD class="summary_val">
$start_time
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>End Time:</B>
</TD>
<TD class="summary_val">
$end_time
</TD>
</TR>
<TR>
<TD class="summary_cat">
<B>Result:</B>
</TD>
<TD class="summary_val">
$result
</TD>
</TR>
</TABLE>
"""
)

STEP_PAGE_TEMPLATE = string.Template(
"""
<HTML>
<HEAD>
<TITLE>$builder_name : Build $build_number : Step $step_name</TITLE>
</HEAD>
<STYLE>
""" +
UI_STYLE +
"""
</STYLE>
<BODY>
<H2>$builder_name : Build $build_number : Step $step_name</H2>
<HR>
$summary_table
<HR>
$file_list
</BODY>
</HTML>
"""
)

STEP_OUTPUT_FILE_TEMPLATE = string.Template(
"""
<P>
<A href="$file_path">$filename</A>
</P>
"""
)

##################################################
##################################################

BUILD_SET_PAGE_BUILDER_ROW_TEMPLATE = string.Template(
"""
<TR class="$status">
  <TD class="stats">
    <A href="$build_url">
    $builder_name
    </A>
  </TD>
  <TD class="stats">
    $status
  </TD>
</TR>
"""
)

BUILD_SET_PAGE_TEMPLATE = string.Template(
"""
<HTML>
<HEAD><TITLE>$scheduler_name : Build Set $build_set_number</TITLE></HEAD>
<STYLE>
""" +
UI_STYLE +
"""
</STYLE>
<BODY>
<H2>$scheduler_name : Build Set $build_set_number</H2>
<HR>
<TABLE>
<TR>
  <TD class="summary_cat">
    <B>Build Reason:</B>
  </TD>
  <TD class="summary_val">
    $reason
  </TD>
</TR>
</TABLE>
<HR>
<TABLE>
<TR>
<TD class="stats"><B>Builder</B></TD>
<TD class="stats"><B>Status</B></TD>
</TR>
$builder_rows
<TABLE>
</BODY>
</HTML>
"""
)

##################################################
##################################################

OUTPUT_FILE_TEMPLATE = string.Template(
"""
<HTML>
<HEAD><TITLE>$file_name</TITLE></HEAD>
<BODY>
<H2>$file_name</H2>
<HR>
<PRE>
$content
</PRE>
</BODY>
</HTML>
"""
)

##################################################
##################################################


SCHEDULER_PAGE = 0
BUILDER_PAGE = 1
BUILD_SET_PAGE = 2
BUILD_PAGE = 3
STEP_PAGE = 4
FILE_PAGE = 5


# Path formats
# 1. <url>:<port>/<sched name>
# 2. <url>:<port>/<sched name>/
# 3. <url>:<port>/<sched name>/<path to a file>
# 4. <url>:<port>/<sched name>/build_sets/<build set no>
# 5. <url>:<port>/<sched name>/build_sets/<build set no>/
# 6. <url>:<port>/<sched name>/<builder name>/builds/<build no>
# 7. <url>:<port>/<sched name>/<builder name>/builds/<build no>/
# 8. <url>:<port>/<sched name>/<builder name>/builds/<build no>/<step name>
# 9. <url>:<port>/<sched name>/<builder name>/builds/<build no>/<step name>/


class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def __init__(self, request, client_address, server,
                 scheduler_name, scheduler_db, builder_db_map,
                 base_url, ui_port):
        self.scheduler_name = scheduler_name
        self.scheduler_db = scheduler_db
        self.builder_db_map = builder_db_map
        self.base_url = base_url
        self.ui_port = ui_port
        BaseHTTPServer.BaseHTTPRequestHandler.__init__(
            self, request, client_address, server)

    def send_ok_header(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def send_error_header(self):
        self.send_response(404)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def validate_path(self):
        parts = self.path.split('/')
        # Skip over the first '/'
        parts = parts[1:]
        if not parts:
            return None
        if parts[0] != self.scheduler_name:
            return None
        if os.path.isfile(os.path.join(*tuple(parts[1:]))):
            return {
                'type': FILE_PAGE,
                'file_path': os.path.join(*tuple(parts[1:]))
            }
        if len(parts) == 1:
            return { 'type': SCHEDULER_PAGE }
        elif len(parts) == 2:
            if parts[1] == '':
                return { 'type': SCHEDULER_PAGE }
            else:
                return None
        elif len(parts) == 3:
            if parts[1] == 'build_sets':
                build_set_no, status = ssutils.to_int(parts[2])
                if not status:
                    return None
                else:
                    return {
                        'type': BUILD_SET_PAGE,
                        'build_set_number': build_set_no
                    }
        elif len(parts) == 4:
            if parts[1] == 'build_sets' and parts[3] == '':
                build_set_no, status = ssutils.to_int(parts[2])
                if not status:
                    return None
                else:
                    return {
                        'type': BUILD_SET_PAGE,
                        'build_set_number': build_set_no
                    }
            elif parts[2] == 'builds':
                builder_name = parts[1]
                build_number, status = ssutils.to_int(parts[3])
                if not status:
                    return None
                else:
                    return {
                        'type': BUILD_PAGE,
                        'builder_name': builder_name,
                        'build_number': build_number
                    }
            else:
                return None
        elif len(parts) == 5:
            builder_name = parts[1]
            build_number, status = ssutils.to_int(parts[3])
            if not status:
                return None
            if parts[2] == 'builds':
                if parts[4] == '':
                    return {
                        'type': BUILD_PAGE,
                        'builder_name': builder_name,
                        'build_number': build_number
                    }
                else:
                    return {
                        'type': STEP_PAGE,
                        'builder_name': builder_name,
                        'build_number': build_number,
                        'step_name': parts[4]
                    }
            else:
                return None
        elif len(parts) == 6:
            builder_name = parts[1]
            build_number, status = ssutils.to_int(parts[3])
            if not status:
                return None
            if parts[2] == 'builds' and parts[5] == '':
                return {
                    'type': STEP_PAGE,
                    'builder_name': builder_name,
                    'build_number': build_number,
                    'step_name': parts[4]
                }
            else:
                return None
        else:
            return None

    def render_scheduler_page(self, descriptor, header_only):
        assert(descriptor['type'] == SCHEDULER_PAGE)
        # TODO(sivachandra): This has to be removed and proper rendering of
        # of the scheduler page should be implemented.
        self.send_error_header()

    def render_build_set_page(self, descriptor, header_only):
        assert(descriptor['type'] == BUILD_SET_PAGE)
        build_set_number = descriptor['build_set_number']
        build_set = self.scheduler_db.get_build_set(build_set_number)
        if not build_set:
            self.send_error_header()
            return
        self.send_ok_header()
        if header_only:
            return
        builder_rows = ''
        for builder in self.builder_db_map:
            builder_db = self.builder_db_map[builder]
            build_number = build_set[builder]
            build = builder_db.get_build(build_set[builder])
            if not build:
                continue
            if build['status'] == ssutils.STATUS_COMPLETED:
                display_status = build['result']
            else:
                display_status = build['status']
            build_url = '%s:%d/%s/%s/builds/%d' % (
                self.base_url, self.ui_port,
                self.scheduler_name, builder, build_number)
            builder_rows += BUILD_SET_PAGE_BUILDER_ROW_TEMPLATE.substitute(
                build_url=build_url,
                status=display_status,
                builder_name=builder,)
        page = BUILD_SET_PAGE_TEMPLATE.substitute(
            scheduler_name=self.scheduler_name,
            build_set_number=build_set_number,
            reason=build_set['reason'],
            builder_rows=builder_rows)
        self.wfile.write(page)

    def render_builder_page(self, descriptor, header_only):
        assert(descriptor['type'] == BUILDER_PAGE)
        # TODO(sivachandra): This has to be removed and proper rendering of
        # of the builder page should be implemented.
        self.send_error_header()

    def render_build_page(self, descriptor, header_only):
        assert(descriptor['type'] == BUILD_PAGE)
        build_number = descriptor['build_number']
        builder_name = descriptor['builder_name']
        builder_db = self.builder_db_map[builder_name]
        build_info = builder_db.get_build(build_number)
        if build_info is None:
            self.send_error_header()
            return
        steps = builder_db.get_all_steps(build_number)
        if steps is None:
            self.send_error_header()
            return
        self.send_ok_header()
        if header_only:
            return
        build_summary = BUILD_SUMMARY_TABLE_TEMPLATE.substitute(
            status=build_info['status'],
            start_time=build_info['start_time'],
            end_time=build_info['end_time'],
            result=build_info['result'])
        step_rows = ''
        for step in steps:
            step_url = '%s:%d/%s/%s/builds/%d/%s/' % (
                self.base_url, self.ui_port, self.scheduler_name, builder_name,
                build_number, step['name'])
            if step['status'] == ssutils.STATUS_COMPLETED:
                display_status = step['result']
            else:
                display_status = step['status']
            step_rows += BUILD_TABLE_ROW_TEMPLATE.substitute(
                step_url=step_url,
                row_class=display_status,
                step_name=step['name'],
                result=step['result'],
                start_time=step['start_time'],
                end_time=step['end_time'])
        build_table = BUILD_TABLE_TEMPLATE.substitute(step_rows=step_rows)
        build_page = BUILD_PAGE_TEMPLATE.substitute(
            builder_name=builder_name,
            build_number=build_number,
            summary_table=build_summary,
            build_table=build_table)
        self.wfile.write(build_page)

    def render_step_page(self, descriptor, header_only):
        assert(descriptor['type'] == STEP_PAGE)
        builder_name = descriptor['builder_name']
        build_number = descriptor['build_number']
        step_name = descriptor['step_name']
        builder_db = self.builder_db_map.get(builder_name)
        if builder_db is None:
            self.send_error_header()
            return
        step_data = builder_db.get_step(build_number, step_name)
        if step_data is None:
            self.send_error_header()
            return
        self.send_ok_header()
        if header_only:
            return
        step_summary = STEP_SUMMARY_TABLE_TEMPLATE.substitute(
            status=step_data['status'],
            result=step_data['result'],
            start_time=step_data['start_time'],
            end_time=step_data['end_time'])
        stdout_path = '%s:%d/%s/%s/builds/%d/%s/stdout.txt' % (
            self.base_url, self.ui_port, self.scheduler_name, builder_name,
            build_number, step_name)
        stderr_path = '%s:%d/%s/%s/builds/%d/%s/stderr.txt' % (
            self.base_url, self.ui_port, self.scheduler_name, builder_name,
            build_number, step_name)
        file_list = STEP_OUTPUT_FILE_TEMPLATE.substitute(file_path=stdout_path,
                                                         filename='stdout.txt')
        file_list += STEP_OUTPUT_FILE_TEMPLATE.substitute(
            file_path=stderr_path,
            filename='stderr.txt')
        step_page = STEP_PAGE_TEMPLATE.substitute(
            builder_name=builder_name,
            build_number=build_number,
            step_name=step_name,
            summary_table=step_summary,
            file_list=file_list)
        self.wfile.write(step_page)

    def render_file_page(self, descriptor, header_only):
        assert(descriptor['type'] == FILE_PAGE)
        self.send_ok_header()
        if header_only:
            return
        file_path = descriptor['file_path']
        file_name = os.path.split(file_path)[1]
        with open(file_path, 'r') as f:
            file_page = OUTPUT_FILE_TEMPLATE.substitute(file_name=file_name,
                                                        content=f.read())
            self.wfile.write(file_page)

    def render_page(self, header_only=False):
        path_descriptor = self.validate_path()
        if path_descriptor:
            path_type = path_descriptor['type']
            if path_type == SCHEDULER_PAGE:
                self.render_scheduler_page(path_descriptor, header_only)
            elif path_type == BUILDER_PAGE:
                self.render_builder_page(path_descriptor, header_only)
            elif path_type == BUILD_SET_PAGE:
                self.render_build_set_page(path_descriptor, header_only)
            elif path_type == BUILD_PAGE:
                self.render_build_page(path_descriptor, header_only)
            elif path_type == STEP_PAGE:
                self.render_step_page(path_descriptor, header_only)
            elif path_type == FILE_PAGE:
                self.render_file_page(path_descriptor, header_only)
            else:
                self.send_error_header()
        else:
            self.send_error_header()

    def do_HEAD(self):
        self.render_page(True)

    def do_GET(self):
        self.render_page()
